import os
import sys
import uuid
import json
import pytest
import types
import importlib

ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if ROOT_DIR not in sys.path:
    sys.path.insert(0, ROOT_DIR)

# 兼容应用中对 app.routes.budget 的导入（实际文件为 app/routes/budgets.py）
# 在调用 create_app 前，注入一个别名模块供 app/__init__.py 使用
try:
    budgets_mod = importlib.import_module('app.routes.budgets')
    alias_mod = types.ModuleType('app.routes.budget')
    alias_mod.budget_bp = getattr(budgets_mod, 'budget_bp')
    # 暴露其他可能需要的符号（可选）
    for name in ('Budget', 'CategoryBudget', 'BudgetType', 'calculate_spent_amount', 'calculate_category_spent'):
        if hasattr(budgets_mod, name):
            setattr(alias_mod, name, getattr(budgets_mod, name))
    sys.modules['app.routes.budget'] = alias_mod
except Exception:
    # 若注入失败，不影响后续逻辑；create_app 时仍可能报错
    pass

from app import create_app
from app.config import Config
import pymysql


class TestingConfig(Config):
    TESTING = True
    # 使用独立的 MySQL 测试数据库，避免接入已有库导致列不一致
    MYSQL_HOST = os.getenv('MYSQL_HOST', 'localhost')
    MYSQL_PORT = int(os.getenv('MYSQL_PORT', '3306'))
    MYSQL_USER = os.getenv('MYSQL_USER', 'root')
    MYSQL_PASSWORD = os.getenv('MYSQL_PASSWORD', 'Rzh20060707')
    TEST_DB_NAME = os.getenv('TEST_DB_NAME', 'bookkeeping_test_pytest')
    SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{MYSQL_HOST}:{MYSQL_PORT}/{TEST_DB_NAME}"
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    JWT_SECRET_KEY = 'test-secret-key'


def _ensure_mysql_database(cfg: TestingConfig):
    # 在连接 SQLAlchemy 之前确保测试数据库为全新创建，避免旧表结构影响
    conn = pymysql.connect(host=cfg.MYSQL_HOST, user=cfg.MYSQL_USER, password=cfg.MYSQL_PASSWORD, port=cfg.MYSQL_PORT)
    try:
        with conn.cursor() as cur:
            cur.execute(f"DROP DATABASE IF EXISTS `{cfg.TEST_DB_NAME}`;")
            cur.execute(f"CREATE DATABASE `{cfg.TEST_DB_NAME}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
        conn.commit()
    finally:
        conn.close()


@pytest.fixture(scope='session')
def app():
    try:
        _ensure_mysql_database(TestingConfig)
    except Exception as e:
        pytest.skip(f"MySQL 测试数据库不可用，请设置 MYSQL_* 环境变量或本地 MySQL 账号。错误: {e}")
    app = create_app(TestingConfig)
    yield app


@pytest.fixture(scope='session')
def client(app):
    return app.test_client()


def _auth_headers(token: str):
    return {'Authorization': f'Bearer {token}'}


@pytest.fixture(scope='session')
def token(client):
    # 注册随机用户以避免重复
    username = f"test_{uuid.uuid4().hex[:8]}@example.com"
    password = '123456'

    resp = client.post('/api/auth/register', json={
        'username': username,
        'password': password,
        'nickname': 'pytest-user'
    })
    assert resp.status_code in (200, 201)

    resp = client.post('/api/auth/login', json={
        'username': username,
        'password': password
    })
    assert resp.status_code == 200
    data = resp.get_json()
    assert 'token' in data
    return data['token']


@pytest.fixture(scope='session')
def expense_category_id(client, token):
    # 获取系统预设分类，选取一个支出分类
    resp = client.get('/api/category', headers=_auth_headers(token))
    assert resp.status_code == 200
    cats = resp.get_json()
    expense = next((c for c in cats if c.get('type') == 'expense'), None)
    assert expense is not None, '没有找到支出分类'
    return expense['id']